package com.esdlife.segment_analytics_plugin.segment_analytics_plugin

import android.content.Context
import android.util.Log
import com.esdlife.segment_analytics_plugin.segment_analytics_plugin.analytics.SegmentAnalyticsServices
import com.esdlife.segment_analytics_plugin.segment_analytics_plugin.analytics.SegmentConstants
import com.esdlife.segment_analytics_plugin.segment_analytics_plugin.analytics.SegmentConstants.Method
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** SegmentAnalyticsPlugin */
class SegmentAnalyticsPlugin : FlutterPlugin, MethodCallHandler {
    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity
    private lateinit var channel: MethodChannel
    private lateinit var applicationContext: Context

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        applicationContext = flutterPluginBinding.applicationContext
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "segment_analytics_plugin")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: Result) {
        when (call.method) {
            Method.INITIAL -> call.useArguments { args ->
                val segmentWriteKey = args[SegmentConstants.SEGMENT_WRITE_KEY] as? String
                if (segmentWriteKey.isNullOrBlank()) {
                    result.errorMessage("Analytics-Initial: writeKey cannot be null or blank.")
                    return@useArguments
                }
                kotlin.runCatching {
                    SegmentAnalyticsServices.initAnalytics(applicationContext, segmentWriteKey)
                }.onSuccess {
                    result.success(SegmentAnalyticsServices.analytics?.toString())
                }.onFailure { err ->
                    result.errorMessage(err.message)
                }
            }
            Method.IDENTIFY -> checkInitialStatusAndGetArgs(call, result) { args ->
                val userId = args[SegmentConstants.USER_ID] as? String
                if (userId == null) {
                    result.errorMessage("Analytics-Identify: userId cannot be null.")
                    return@checkInitialStatusAndGetArgs
                }

                runCatching(result) {
                    SegmentAnalyticsServices.identify(
                        userId = userId,
                        params = args.getParams(),
                    )
                }
            }
            Method.TRACK -> checkInitialStatusAndGetArgs(call, result) { args ->
                val name = args[SegmentConstants.NAME] as? String
                if (name.isNullOrBlank()) {
                    result.errorMessage("Analytics-Track: name cannot be null or blank.")
                    return@checkInitialStatusAndGetArgs
                }

                runCatching(result) {
                    SegmentAnalyticsServices.track(
                        name = name,
                        params = args.getParams(),
                    )
                }
            }
            Method.SCREEN -> checkInitialStatusAndGetArgs(call, result) { args ->
                val title = args[SegmentConstants.TITLE] as? String
                if (title == null) {
                    result.errorMessage("Analytics-Screen: title cannot be null.")
                    return@checkInitialStatusAndGetArgs
                }
                runCatching(result) {
                    SegmentAnalyticsServices.screen(
                        title = title,
                        params = args.getParams(),
                        category = args[SegmentConstants.CATEGORY] as? String,
                    )
                }
            }
            Method.GROUP -> checkInitialStatusAndGetArgs(call, result) { args ->
                val groupId = args[SegmentConstants.GROUP_ID] as? String
                if (groupId == null) {
                    result.errorMessage("Analytics-Group: groupId cannot be null.")
                    return@checkInitialStatusAndGetArgs
                }
                runCatching(result) {
                    SegmentAnalyticsServices.group(
                        groupId = groupId,
                        params = args.getParams(),
                    )
                }
            }
            else -> result.notImplemented()
        }
    }

    private fun checkInitialStatusAndGetArgs(
        call: MethodCall,
        result: Result,
        action: (HashMap<String, Any>) -> Unit
    ) {
        val analytics = SegmentAnalyticsServices.analytics
        if (analytics == null) {
            result.errorMessage("Segment Analytics not initialized.")
            return
        }
        call.useArguments(action)
    }

    private fun runCatching(result: Result, action: () -> Unit) {
        kotlin.runCatching {
            action()
        }.onSuccess {
            result.success(null)
        }.onFailure { err ->
            result.errorMessage(err.message)
        }
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
        SegmentAnalyticsServices.shutdown()
    }
}


private fun Result.errorMessage(message: String?) {
    if (!message.isNullOrBlank()) {
        Log.e("SegmentAnalyticsPlugin", message)
    }
    error("0", message, null)
}

private fun MethodCall.useArguments(action: (HashMap<String, Any>) -> Unit) {
    if (arguments is HashMap<*, *>) {
        val args = arguments as? HashMap<String, Any>
        if (args != null) {
            action(args)
        }
    }
}

private fun HashMap<String, Any>.getParams(): HashMap<String, Any>? {
    return this[SegmentConstants.PARAMS] as? HashMap<String, Any>
}

